package com.autumn.juc.lock;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 进行两种测试
 * 关闭逃逸分析，同时调大堆空间，避免堆内 GC 发生，若有 GC 信息则会打印出来
 * VM 运行参数：
 * -Xmx4G -Xms4G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
 * 开启逃逸分析，JDK8 默认开启
 * VM 运行参数：
 * -Xmx4G -Xms4G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
 * 执行 main 方法后
 * jps 查看进程
 * jmap -histo 进程 ID
 */
public class EscapeTest {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 500000; i++) {
            alloc();
        }

        long end = System.currentTimeMillis();

        System.out.println(">>>>>>>>>> 执行时间：" + (end - start) + " ms");
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * JIT 编译时会对代码进行逃逸分析
     * 并不是所有对象都存放在堆区，有的一部分存放在线程栈空间
     * Point 没有逃逸
     *
     * @return
     */
    private static String alloc() {
        Point point = new Point();
        return point.toString();
    }

    /**
     * 同步省略（锁消除）JIT 编译阶段优化，JIT 经过逃逸分析后发现无线程安全问题，则会消除锁
     *
     * @param str1
     * @param str2
     */
    public void append(String str1, String str2) {
        StringBuffer buffer = new StringBuffer();
        buffer.append(str1).append(str2);
    }

    /**
     * 标量替换
     */
    private static void test() {
        Point point = new Point(1, 2);
        System.out.println(">>>>>>>>>> point.x=" + point.getX() + "；point.y=" + point.getY());
        // point在方法内部，没有逃逸出方法
        // 此时getter时，x和y不会在堆内另外分配内存存放
        // 即进行标量替换，相当于直接替换成:
        //      int x = 1;
        //      int y = 2;
        //      System.out.println(">>>>>>>>>> point.x=" + x + "；point.y=" + y);
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Point {
    private int x;
    private int y;
}
